package ch.mobi.osgi2maven2.osgi;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.osgi.framework.Version;

import com.google.code.mjl.Log;
import com.google.code.mjl.LogFactory;
import com.springsource.util.osgi.manifest.BundleManifest;
import com.springsource.util.osgi.manifest.BundleManifestFactory;

public class OsgiBundle {

	private static final Log logger = LogFactory.getLog();

	private BundleManifest bundleManifest;
	private final File bundleFile;

	public OsgiBundle(File file) throws IOException {

		this.bundleFile = file;
		Reader reader = createBundleReader(file);

		try {
			bundleManifest = BundleManifestFactory.createBundleManifest(reader);
		} finally {
			reader.close();
		}

	}

	private Reader createBundleReader(File file) throws FileNotFoundException, IOException {
		Reader reader;
		if (file.isDirectory()) {
			File manifestFile = new File(file, "META-INF/MANIFEST.MF");
			reader = new InputStreamReader(new FileInputStream(manifestFile), "UTF-8");
		} else {
			JarFile jarFile = new JarFile(file);
			ZipEntry entry = jarFile.getEntry("META-INF/MANIFEST.MF");
			InputStream inputStream = jarFile.getInputStream(entry);
			reader = new InputStreamReader(inputStream, "UTF-8");
		}
		return reader;
	}

	public String getBundleName() {
		return bundleManifest.getBundleSymbolicName().getSymbolicName();
	}

	public Version getVersion() {
		return bundleManifest.getBundleVersion();
	}

	public List<RequiredBundle> getRequiredBundles() {
		ArrayList<RequiredBundle> result = new ArrayList<RequiredBundle>();
		List<com.springsource.util.osgi.manifest.RequiredBundle> requiredBundles = bundleManifest.getRequireBundle().getRequiredBundles();
		for (com.springsource.util.osgi.manifest.RequiredBundle requiredBundle : requiredBundles) {
			result.add(new RequiredBundle(requiredBundle.getBundleSymbolicName(), requiredBundle.getBundleVersion()));
		}
		return result;

	}

	@Override
	public String toString() {
		return "OsgiBundleFile [bundleName=" + getBundleName() + ", version=" + getVersion() + "]";
	}

	public boolean isRequiredBundle(RequiredBundle current) {

		return this.getBundleName().equals(current.getSymbolicName()) && current.getVersionRange().includes(this.getVersion());
	}

	public File getBundleFile() {
		return bundleFile;
	}

	public void copyJarToDirectory(File directory) throws IOException {
		File destination = new File(directory, jarFileName());
		if (!bundleFile.isDirectory()) {
			logger.debug("copying jar '%s' to '%s'", bundleFile.getAbsoluteFile(), destination.getAbsoluteFile());
			FileUtils.copyFile(bundleFile, destination);
		} else {
			logger.debug("copying jar contents from '%s' to jar '%s'", bundleFile.getAbsoluteFile(), destination.getAbsoluteFile());
			JarOutputStream destinationJarOutputStream = new JarOutputStream(new FileOutputStream(destination));
			copyBundleClasspathContentToSingleJar(destinationJarOutputStream, bundleManifest.getBundleClasspath());
			destinationJarOutputStream.close();
		}
	}

	private void copyBundleClasspathContentToSingleJar(JarOutputStream destinationJarOutputStream, List<String> bundleClasspath) throws IOException {
		if (bundleClasspath != null) {
			HashSet<String> alreadyCreatedEntries = new HashSet<String>();
			for (String classpathEntry : bundleClasspath) {
				copyJarContentToDestinationJar(classpathEntry, destinationJarOutputStream, alreadyCreatedEntries);
			}
		}
	}

	private void copyJarContentToDestinationJar(String classpathEntry, JarOutputStream destinationJarOutputStream, HashSet<String> alreadyCreatedEntries) throws IOException {
		File jarPath = new File(bundleFile, classpathEntry);
		logger.debug("copying content of %s", jarPath.getAbsolutePath());
		if (jarPath.exists()) {
			JarFile sourceJar = new JarFile(jarPath);
			Enumeration<JarEntry> entries = sourceJar.entries();
			while (entries.hasMoreElements()) {
				JarEntry entry = entries.nextElement();
				if (!alreadyCreatedEntries.contains(entry.getName())) {
					destinationJarOutputStream.putNextEntry(entry);
					alreadyCreatedEntries.add(entry.getName());
					IOUtils.copy(sourceJar.getInputStream(entry), destinationJarOutputStream);
				}
			}
		} else {
			logger.warn("file %s does not exist!", jarPath);
		}
	}

	public String jarFileName() {
		return getBundleFileName() + ".jar";
	}

	public String getBundleFileName() {
		Version version = getVersion();
		return String.format("%s-%d.%d.%d", getBundleName(), version.getMajor(), version.getMinor(), version.getMicro());
	}

}
